package com.linqingying.gitcode


import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.progress.*
import com.intellij.openapi.util.Computable
import com.intellij.openapi.util.NlsContexts.ProgressDetails
import com.intellij.openapi.util.NlsContexts.ProgressText
import com.intellij.platform.util.progress.*
import kotlinx.coroutines.*
import kotlin.coroutines.AbstractCoroutineContextElement
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.cancellation.CancellationException
import kotlin.coroutines.coroutineContext


private class ModalityStateElement(val modalityState: ModalityState) :
    AbstractCoroutineContextElement(ModalityStateElementKey) {

    override fun toString(): String {
        return modalityState.toString()
    }
}

private object ModalityStateElementKey
    : CoroutineContext.Key<ModalityStateElement>

fun CoroutineContext.contextModality(): ModalityState? {
    return this[ModalityStateElementKey]?.modalityState
}

sealed interface RawProgressReporterHandle : AutoCloseable {

    val reporter: RawProgressReporter

    override fun close()
}

interface RawProgressReporter {

    /**
     * Updates the current progress text.
     */
    fun text(text: @ProgressText String?): Unit = Unit

    /**
     * Updates the current progress details.
     */
    fun details(details: @ProgressDetails String?): Unit = Unit

    /**
     * Updates the current progress fraction.
     *
     * @param fraction a number between 0.0 and 1.0 reflecting the ratio of work that has already been done (0.0 for nothing, 1.0 for all),
     * or `null` to clear the fraction and make the progress indeterminate
     */
    fun fraction(fraction: Double?): Unit = Unit
}

@Throws(CancellationException::class)
private fun <T> contextToIndicator(ctx: CoroutineContext, action: () -> T): T {
    val job = ctx.job
    job.ensureActive()
    val contextModality = ctx.contextModality() ?: ModalityState.nonModal()
//    val handle = ctx.internalCreateRawHandleFromContextStepIfExistsAndFresh()
    /*      return if (handle != null) {
           handle.use {
               val indicator = RawProgressReporterIndicator(handle.reporter, contextModality)
               jobToIndicator(job, indicator, action)
           }
       } else {*/
    val indicator = EmptyProgressIndicator(contextModality)
    return jobToIndicator(job, indicator, action)
//    }
}

fun <T> jobToIndicator(job: Job, indicator: ProgressIndicator, action: () -> T): T {
    try {
        return ProgressManager.getInstance().runProcess(Computable {
            // Register handler inside runProcess to avoid cancelling the indicator before even starting the progress.
            // If the Job was canceled while runProcess was preparing,
            // then CompletionHandler is invoked right away and cancels the indicator.
            @OptIn(InternalCoroutinesApi::class)
            val completionHandle = job.invokeOnCompletion(onCancelling = true) {
                if (it is CancellationException) {
                    indicator.cancel()
                }
            }
            try {
                indicator.checkCanceled()
                action()
            } finally {
                completionHandle.dispose()
            }
        }, indicator)
    } catch (e: ProcessCanceledException) {
        if (job.isCancelled) {
            @OptIn(InternalCoroutinesApi::class)
            throw job.getCancellationException()
        }
        throw PceCancellationException(e)
    }
}

class PceCancellationException(pce: ProcessCanceledException) : java.util.concurrent.CancellationException() {
    init {
        initCause(pce)
    }

    @get:Synchronized
    override val cause: ProcessCanceledException
        get() = super.cause as ProcessCanceledException
}

suspend fun <T> coroutineToIndicator(action: () -> T): T {
    val ctx = coroutineContext
    return contextToIndicator(ctx, action)
}
